<template>
    <div class="file-upload-wrapper">
        <ul class="file-upload-list" v-if="showFileList && dataList.length > 0">
			<li 
			    :class="`file-upload-list__item ${listType === 'picture-card' ? 'item-picture-card' : 'item-picture-transverse'}`" 
			    v-for="(item, index) in dataList"
			    :key="index">
                <template v-if="defaultOptions.type === 'image'">
				    <img :src="`${item.url}`" class="file-upload-list__item-thumbnail">
                    <span 
                        class="file-upload-list__item-preview" 
                        @click.stop="preview(item)" 
                        title="预览">
                        <i class="el-icon-zoom-in"></i>
                    </span>
                    <span class="file-upload-list__item-name">{{ item.name }}</span>
                </template>
                <span 
                    class="file-upload-list__item-name" 
                    @click.stop="preview(item)" 
                    title="预览" 
                    v-else>
                    {{ item.name }}
                </span>
                <span 
                    class="file-upload-list__item-delete" 
                    @click.stop="handleRemove(index)" 
                    title="删除">
                    <i class="el-icon-delete"></i>
                </span>
			</li>
		</ul>
        <!--
            limit 若为 0 则 el-upload 视为不限制
        -->
		<!-- <div :class="{'file-upload__selected': true, 'item-picture-card': listType === 'picture-card'}"> -->
		<div :class="[
                'file-upload__selected', 
                showFileList ? (
                    listType === 'picture-card' ? 
                    'item-picture-card' : 
                    'item-picture-transverse'
                ) : ''
            ]">
            <el-upload 
                action="#"
                ref="elUploadRef"
                :http-request="handleOssUpload"
                :auto-upload="ossUpload"
                :multiple="multiple"
                :limit="defaultOptions.limit || -1" 
                :file-list="fileList"
                :before-upload="handleBeforeUpload"
                :on-change="handleUploadChange"
                :on-exceed="handleExceed"
                :show-file-list="false"
                :accept="defaultOptions.accept.join(', ')">
                <slot name="default"></slot>
                <el-button 
                    v-if="defaultOptions.limit - this.dataList.length <= 0" 
                    class="upload-invalid" 
                    @click.stop="handleExceed">
                </el-button>
            </el-upload>
		</div>
        <!-- 文件预览 -->
        <file-preview ref="filePreviewRef"></file-preview>
    </div>
</template>
<script>
import BenzAMRRecorder from 'benz-amr-recorder'
import { randomString } from '@/common/js/util.js'
import filePreview from '@/components/home/file-preview.vue'
import store from '@/store'
export default {
    name: 'file-upload',
    components: { filePreview },
    props: {
        type: String,                   // 类型
        multiple: {                     // 是否支持多选
            type: Boolean,
            default: false
        },
        limit: Number,                  // 数量限制
        listType: String,               // 'picture-card' - 照片墙，否则为文件列表 在showFileList = true时有效
        ossUpload: {                    // 是否OSS上传, 不为OSS上传则直接返回文件对象
            type: Boolean,
            default: false
        },
        showFileList: Boolean,          // 是否显示文件列表
        maxDuration: Number,            // 时长限制
        maxPixel: Number,               // 像素限制
        maxSize: Number,                // 大小限制
        fileList: {                     // 文件列表
            type: Array,
            default: () => ([])
        },
        save: {                         // 是否进行素材保存
            type: Boolean,
            default: true
        },
        onChange: {                     // 素材文件状态改变时钩子：添加、删除
            type: Function,
            default(file, fileList) {}
        },
        onExceed: Function              // 个数超限制钩子
    },
    data() {
        return {
            typeList: [                 // 类型组
                {
                    label: '图片', 
                    value: 1, 
                    type: 'image',
                    accept: ['.PNG', '.JPG', '.GIF', '.BMP'], 
                    maxSize: 2,         // 最大文件大小（单位M）
                    maxPixel: 0,        // 最大像素, 0 - 不限制
                    limit: 1
                },
                { 
                    label: '视频', 
                    value: 2, 
                    type: 'video', 
                    accept: ['.MP4'], 
                    maxSize: 10,        // 最大文件大小（单位M）
                    maxDuration: 0,     // 最大时长（单位S）, 0 - 不限制
                    limit: 1,
                    cover: {            // 封面
                        accept: ['.PNG', '.JPG', '.GIF', '.BMP'], 
                        maxSize: 2
                    } 
                },
                { 
                    label: '音频', 
                    value: 5, 
                    type: 'audio',
                    accept: ['.AMR', '.MP3'], 
                    maxSize: 20,        // 最大文件大小（单位M）
                    maxDuration: 0,     // 最大时长（单位S）, 0 - 不限制
                    limit: 1
                },
                { 
                    label: '文件', 
                    value: 4, 
                    type: 'file',
                    accept: ['.PDF', '.DOC', '.DOCX', '.XLS', '.XLSX'], 
                    maxSize: 20, 
                    limit: 1
                },
            ],
            defaultOptions: {
                type: 'image',          // 类型 image - 图片, video - 视频，audio - 音频，file - 文件
                limit: -1,              // 最大可选取素材个数 -1 表示不限制
                maxDuration: 0,         // 素材时长限制, 针对视频、音频的限制, 0 表示不限制
                maxPixel: 0,            // 素材像素时长限制, 针对图片的限制, 0 表示不限制
                maxSize: 0,             // 素材大小限制, 0 表示不限制,
                accept: []              // 文件类型
            },
            dataList: []                // 素材列表
        }
    },
    watch: {
        "fileList": {
            immediate: true,
            handler: function(val) {
                this.dataList = val || []
            }
        },
        "limit": {
            immediate: false,
            handler(val) {
                this.defaultOptions.limit = val // 若为0则el-upload视为不限制
            }
        }
    },
    created() {
        this.defaultOptions = {
            ...this.typeList.find(item => item.type == this.type),
            limit: this.limit
        }
        if (this.maxDuration) this.defaultOptions.maxDuration = this.maxDuration
        if (this.maxSize) this.defaultOptions.maxSize = this.maxSize
        if (this.maxPixel) this.defaultOptions.maxPixel = this.maxPixel
    },
    methods: {
        handleExceed() {
            let { limit } = this.defaultOptions
            this.$message.error('上传已超数量限制, 剩余可上传数量为：' + (limit - this.dataList.length))
        },
        async handleUploadChange(file, fileList) { // 针对不走auto-upload自动上传时执行
            if (this.ossUpload) return
            try {
                await this.handleUploadCheck(file)
                if (!file.url) {
                    file.url = window.URL.createObjectURL(file.raw)
                }
                file.type = this.defaultOptions.type
                this.dataList.push(file)
                this.handleChange()
            } catch (error) {
                error && this.$message.error(`${error}`)
                fileList.pop()
            }
        },
        async handleBeforeUpload(file) { // auto-upload = false 则不执行 before-upload
            try {
                await this.handleUploadCheck(file)
                this.file = file
                return Promise.resolve()
            } catch (error) {
                error && this.$message.error(`${error}`)
                return Promise.reject()
            }
        },
        async handleUploadCheck(file) {
            try {
                let { label, accept, type, maxSize, maxPixel, maxDuration } = this.defaultOptions
                const fileSuffix = file.name.substring(file.name.lastIndexOf('.'))
                const acceptValid = accept.includes(fileSuffix.toUpperCase())
                const sizeValid = file.size / 1024**2 <= maxSize
                // 格式限制
                if (!acceptValid) {
                    return Promise.reject('仅支持(' + accept.join(", ").replace(/\./g, '') + ')格式!')
                }
                // 大小限制
                if (!sizeValid) {
                    return Promise.reject(`${label}大小不能超过${maxSize}MB`)
                }
                // 计算时长
                if (+maxDuration > 0) {
                    this.handleLoading().start()
                    const audioDuration = await new Promise((resolve, reject) => {
                        const url = window.URL.createObjectURL(file.raw || file)
                        if (fileSuffix.toUpperCase() !== '.AMR') {
                            const audioElement = new Audio(url)
                            audioElement.addEventListener("loadedmetadata", function() {
                                loading.close()
                                resolve(Math.ceil(audioElement.duration) || 0)
                            })
                        } else {
                            const amr = new BenzAMRRecorder()
                            amr.initWithUrl(url).then(function() {
                                resolve(Math.ceil(amr.getDuration()))
                            }).catch(() => {
                                reject()
                            }).finally(() => {
                                this.handleLoading().close()
                            })
                        }
                    })
                    // 限制时长
                    if (+maxDuration > 0 && audioDuration > maxDuration) {
                        return Promise.reject(`${label}时长不能超过${maxDuration}s`)
                    } else {
                        file.duration = audioDuration
                        return Promise.resolve()
                    }
                }
                // 若为图片，则判断像素
                if (type === 'image') {
                    const img = new Image()
                    img.crossOrigin = "Anonymous"
                    img.src = window.URL.createObjectURL(file.raw || file)
                    const imgPixel = await new Promise((resolve, reject) => {
                        img.onload = function() {
                            resolve(img.width * img.height)
                        }
                        img.error = function() {
                            reject('image loading failed')
                        }
                    })
                    if (+maxPixel > 0 && imgPixel > maxPixel) {
                        return Promise.reject(`${label}像素不能超过${maxPixel}`)
                    } else {
                        file.pixel = imgPixel
                        return Promise.resolve()
                    }
                }
                return Promise.resolve()
            } catch (error) {
                return Promise.reject(error)
            }
        },
        async handleOssUpload() {
            try {
                const file = this.file
                if (!this.ossParams) {
                    // 获取OSS上传签名参数
                    this.ossParams = await this.$http.post(
                        '/admin/materialLibrary/index.php?act=oss&op=getSignature', 
                        { type }
                    )
                }
                const { dir, accessid, policy, callback, signature, host } = this.ossParams
                const type     = this.defaultOptions.type
                const fileName = file.name
                const suffix   = fileName.substring(fileName.lastIndexOf('.') + 1)
                const picName  = randomString(10) + '.' + suffix
                const keyValue = dir + picName
                const formData = new FormData()
                formData.append('name', fileName)           // 文件名称
                formData.append('key', keyValue)            // 存储在oss的文件路径
                formData.append('OSSAccessKeyId', accessid) // accessKeyId
                formData.append('policy', policy)           // policy
                formData.append('callback', callback)       // 回调地址
                formData.append('Signature', signature)     // 签名
                formData.append('success_action_status', 200)
                formData.append('file', file.raw || file, fileName)
                this.handleLoading().start()
                /**
                 * 开发环境下通过增加 "/" 然后通过本地代理 解决OSS上传跨域问题
                 */
                const _host = store.state.isServer ? host : '/' + host
                const res = await this.$http.post(_host, formData, { 
                    headers: { 'Content-Type': 'multipart/form-data' }
                })
                if (+res.code === 200) {
                    const url = host + '/' + res.data.filename
                    const newFile = {
                        type,
                        name: picName, 
                        file_name: fileName, 
                        url, 
                        ...res.data 
                    }
                    if (!isNaN(+file.duration)) newFile.duration = file.duration
                    if (type === 'image') newFile.pixel = file.pixel

                    if (this.save !== false) {
                        // 进行素材保存
                        const res2 = await this.$http.post('/admin/materialLibrary/index.php?act=materialLibrary&op=upload', {
                            file: JSON.stringify([{
                                ...newFile,
                                uploaded: true
                            }])
                        })
                        if (res2.code == 200) {
                            newFile.id = res2.data.id
                            this.dataList.push(newFile)
                            this.handleChange()
                        } else {
                            throw res2.msg
                        }
                    } else {
                        this.dataList.push(newFile)
                        this.handleChange()
                    }
                } else {
                    throw fileName + '上传失败'
                }
            } catch (error) {
                error && this.$message.error(`${error}`)
                // 上传失败则从当前 fileList 中删除该素材
                const index = this.$refs.elUploadRef.uploadFiles.findIndex(item => item.uid === file.uid)
                this.$refs.elUploadRef.uploadFiles.splice(index, 1)
            } finally {
                this.handleLoading().close()
            }
        },
        handleLoading() {
            return {
                loading: null,
                start: () => {
                    this.loading = this.$loading({
		            	lock: true,
		            	text: 'Loading',
		            	spinner: 'el-icon-loading',
		            	background: 'rgba(0, 0, 0, 0.7)'
		            })
                },
                close: () => {
                    this.loading.close()
                }
            }
        },
        handleChange() {
            if (typeof this.onChange == 'function') {
                this.onChange(this.dataList)
            }
        },
        handleRemove(index) {
            const { id, name } = this.dataList[index]
            this.$alert(
                '确定删除'+ name +'吗?', 
                '温馨提示', 
                {
                    type: 'warning',
                    showCancelButton: true
                }
            ).then(() => {
                this.$http.post('/admin/materialLibrary/index.php?act=materialLibrary&op=del', { ids: id }).
                then(res => {
                    if (res.code == 200) {
                        this.dataList.splice(index, 1)
                        this.handleChange()
                        this.$message.success('删除成功')
                    } else {
                        throw '删除失败'
                    }
                }).catch(error => {
                    error && this.$message.error(`${error}`)
                })
            }).catch(error => {})
        },
        handleClose() {
            this.$refs.libraryRef.resetChecked()
            this.dialogVisible = false
        },
        handleConfirm() {
            this.dataList.push(...this.checkedList)
            this.checkedList = []
            this.handleChange()
            this.handleClose()
        },
        preview(file) {
            this.$refs.filePreviewRef.preview(file)
        }
    }
}
</script>
<style lang="scss">
.file-upload-wrapper {
    position: relative;
    .file-upload-list {
        display: inline;
        font-size: 14px;
        list-style-type: none;
        padding: 0;
        margin: 0;
    }
    .file-upload-list__item {
        position: relative;
    }
    .item-picture-transverse {
        padding: 10px;
        box-sizing: border-box;
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 10px;
        background-color: #ECF5FF;
        border: 1px solid #409EFF;
        border-radius: 3px;
    }
    .item-picture-card {
        width: 160px;
        height: 128px;
        line-height: 128px;
        background-color: #FAFBFC;
        border: 1px dashed #c0ccda;
        border-radius: 6px;
        box-sizing: border-box;
        vertical-align: top;
        display: inline-block;
        outline: 0;
        overflow: hidden;
    	margin: 0 10px 10px 0;
    }
    .file-upload-list__item-name {
        display: none;
        flex-grow: 1;
        font-size: 12px;
        word-break: break-all;
        word-wrap: break-word;
        white-space: pre-wrap;
        line-height: 140%;
        padding: 0 10px;
    }
    .item-picture-transverse .file-upload-list__item-name {
        display: block;
    }
    .item-picture-transverse .file-upload-list__item-thumbnail {
        width: 40px;
        height: 40px;
    }
    .item-picture-card .file-upload-list__item-thumbnail {
        width: 100%;
        height: 100%;
    }
    .file-upload-list__item-preview, 
    .file-upload-list__item-delete {
        position: absolute;
        width: 100%;
        background: rgba(0, 0, 0, .5);
        text-align: center;
        color: #fff;
        transition: all .2s cubic-bezier(.645, .045, .355, 1);
        z-index: 5;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        height: 26px;
        opacity: 0;
    }
    .file-upload-list__item:hover .file-upload-list__item-preview,
    .file-upload-list__item:hover .file-upload-list__item-delete {
        opacity: 1;
    }
    .file-upload-list__item-preview i, 
    .file-upload-list__item-delete i {
        font-size: 16px;
        color: #fff;
    }
    .item-picture-card .file-upload-list__item-preview {
        bottom: -100%;
    }
    .item-picture-card:hover .file-upload-list__item-preview {
        bottom: 0;
    }
    .item-picture-card .file-upload-list__item-delete {
        top: -100%;
    }
    .item-picture-card:hover .file-upload-list__item-delete {
        top: 0;
    }
    .item-picture-transverse .file-upload-list__item-preview,
    .item-picture-transverse .file-upload-list__item-delete {
        width: 40px;
    }
    .item-picture-transverse .file-upload-list__item-preview {
        top: 10px;
        left: 10px;
        height: 40px;
    }
    .item-picture-transverse .file-upload-list__item-delete {
        height: 100%;
        top: 0;
        right: 0;
    }
    .file-upload__selected {
        font-size: 14px;
        position: relative;
        div, .el-upload {
            width: 100%;
        }
        &.item-picture-card {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            transition: border .3s;
            text-align: center;
            cursor: pointer;
            outline: 0;
            i.selected-icon {
                font-size: 22px;
                color: #409EFF;
                text-align: center;
            }
            div, .el-upload {
                height: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
            }
        }
        &.item-picture-card:hover,
        &.item-picture-transverse:hover {
            border-color: #409EFF;
        }
        &.item-picture-transverse {
            background-color: #F2F3FF;
            border: 1px dashed #c0ccda;
        }
        .upload-invalid {
            position: absolute;
            left: 0;
            top: 0;
            right: 0;
            bottom: 0;
            opacity: 0;
        }
    }
}
</style>